iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
自我挑戰組

JavaScript 奇奇怪怪的核心觀念系列 第 22

(Day 22) ES6 的 let 、const

  • 分享至 

  • xImage
  •  

前言

在 ES6 新增兩種變數方法 letconst ,不過我們比較常把 const 叫做常數,主要是因為 var 的特性 容易觸發 Bug , 這邊與 var 的差異主要有:

  • 作用域範圍不同
  • 提升特性不同
  • 全域不掛在 window 下

在提及這些不同特性之前,先大致介紹一下這兩個用法的差別。

let 、 const 基本介紹

let 能夠重新賦值,但不能重複宣告

let String1 = 'test1'
String1 =  'test2'
let String1 = 'test3' // 重複宣告錯誤, Uncaught SyntaxError: Identifier 'String1' has already been declared

const 不能重新賦值,也不能重新宣告。

const String1 = 'test1'
const String1 = 'test2' //  重複宣告錯誤, Uncaught SyntaxError: Identifier 'String1' has already been declared
String1 = 'test3' // 賦值錯誤, Uncaught TypeError: invalid assignment to const 'String1'

要補充一點如果 const 的值是物件,對物件底下的屬性賦值, const 則能接受這種操作繼續使用。

const obj = {}
obj.name = 'Ryder'
console.log(obj) // { name: 'Ryder' }

const array = []
array.push(1)
console.log(array) // [1]

作用域不同

在過去 var 作用域是根據函式作用域,而 letconst 則是以 { ... } Block 區塊做為作用域,來看看以下範例:

var name1 = 'Ryder'
let name2 = 'Ryder'

function test(){
	var name1 = 'Jack'
	let name2 = 'Jack'
}
test()
console.log(name1, name2)  // ?

結果是 Ryder, Ryder ,這範例很好理解,不論是 var 還是 letname = 'Jack' 的作用範圍都只在 test() 這個函式中,console 的位置則是在全域,自然都會是 Ryder ,那麼再來看看這個範例:

var name1 = 'Ryder'
let name2 = 'Ryder'

{
 var name1 = 'Jack'
}

{
 let name2 = 'Jack'
}
console.log(name1 , name2)  // ?

結果會是 Jack, Ryder ,這是因為上面提到的, let 是以 { ... } 區塊做為作用域,因此 let name1 = 'Jack' 這個語法的有效範圍只會存在於 { ... } 之中,而 var 則會被 { ... } 中的 var name1 = 'Jack' ,直接做替換,因此 name1 會是 'Jack'

這邊要補充一下, 這邊提到的 { ... } 並不是物件,而是一個作用域範圍,主要是為了搭配 letconst 特性 ES6 才引入的,不過實做中通常不會刻意使用 { ... } 去區分作用域。

提升特性不同

在提升章節我們有提到,JavaScript 在編譯程式碼時,會分為兩個階段:
1.創造階段
2.執行階段
var 變數會先在 創造階段 被建立,進入執行階段才會實際賦值,而在創造階段中的 var 變數,他的值會是 undefined ,如這個範例:

console.log(name1)  // undefined
var name1 = 'Ryder'

let 雖然也有提升概念,也同樣分成:
1.創造階段
2.執行階段
但在創造階段和 var 不同,let 在創造階段不是直接顯示 undefined ,他是進入一個 暫時性死區 (TDZ) 的狀況,MDN 文件是這麼描述的:

The variable is in a "temporal dead zone" from the start of the block until the initialization is processed

如果我們在 暫時性死區 的狀態去取得 let 變數的值,瀏覽器會跳錯,要注意的是,不同瀏覽器跳出的錯誤訊息會不同,如下範例:

FireFox 版本

console.log(name2)  // Uncaught ReferenceError: can't access lexical declaration 'name1' before initialization
let name2 = 'Ryder'

Chrome 版本

console.log(name2)  //Uncaught ReferenceError: name1 is not defined
let name2 = 'Ryder'

全域建立變數

上面有提到到 letconst 是根據 { ... } 來區分作用域的,這邊要提一點的是 var 全域建立時,會是掛在 window 下。

而使用 letconst 在全域建立變數時,他並不會掛在 window 下,但我們若直接呼叫變數,他也會正確顯示,如範例

var name1= 'Ryder'
let name2 = 'Ryder'

window.name1 // 'Ryder'
window.name2 // undefined

name1 // 'Ryder'
name2 // 'Ryder'

這個原因是出在全域執行環境(Global space) 上面,首先這個全域執行環境其實是由兩個環境所組成的

  • 全域物件 - Object Env
  • 宣告環境 - Declare Env

因此全域執行環境(Global space) 其實是一個由雙環境組成的東西,一般來說我們是看不到 Declare Env 的。

所以 var 其實是基於 ObjectEnv 宣告並加入到 Declare Env,而 letconst 則是只會宣告在 Declare Env 中,這也就是為什麼我們無法在 Window 上面看到由 letconst 宣告的變數但卻又可以正常取得到值的原因。

參考文獻


上一篇
(Day 21) ES6 class 語法糖
下一篇
(Day23) ES6 箭頭函式
系列文
JavaScript 奇奇怪怪的核心觀念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言